import { VxeGridInstance } from 'vxe-table'

import { isArray, clone } from 'xe-utils'

import { getVxeGlobalConfig } from '../setting/globalSetting'

type ReturnPromiseType<T extends PromiseFn> = Awaited<ReturnType<T>>

export function getTableRowKey($grid: VxeGridInstance) {
  const { props } = $grid
  const { rowId, rowConfig } = props
  const globalKeyField = getVxeGlobalConfig().table?.rowConfig?.keyField
  return rowId || rowConfig?.keyField || globalKeyField || '_X_ROW_KEY'
}

export function removeTableRowKey<T extends Recordable>(
  grid: VxeGridInstance<T>,
  data: T[],
  isClone?: boolean,
  rowKey?: string
): T[]
export function removeTableRowKey<T extends Recordable>(
  grid: VxeGridInstance<T>,
  data: T,
  isClone?: boolean,
  rowKey?: string
): T
export function removeTableRowKey<T extends Recordable>(
  $grid: VxeGridInstance<T>,
  data: T[] | T,
  isClone = false,
  rowKey?: string
) {
  if (isClone) {
    data = clone(data, true)
  }
  rowKey = rowKey || getTableRowKey($grid)
  if (isArray(data)) {
    data.forEach((item) => {
      Reflect.deleteProperty(item, rowKey)
    })
  } else {
    Reflect.deleteProperty(data, rowKey)
  }
  return data
}

// 不使用 insertNextAt, 函数内部有顺序bug
// v4.7.84 : https://github.com/x-extends/vxe-table/issues/2589
/**
 * 新增行
 * 新增位置为目标行的下一行(显示为准) insertAt不支持以被隐藏的行作为插入位置
 * @param $grid
 * @param options.targetRow 目标行 只对 mode=row 有效
 * @param options.targetIndex 目标索引 只对 mode=dataIndex, visibleIndex 有效
 * @param options.newData 新增数据 默认为空对象
 * @param options.rowKey 行主键 非必要不传,自动获取
 * @param options.mode 目标行定位模式
 * @param options.isClone 是否深拷贝数据
 */
export async function createTableRow<T extends Recordable>(
  $grid: VxeGridInstance<T>,
  {
    targetRow,
    targetIndex = -1,
    newData = {} as T,
    rowKey = getTableRowKey($grid),
    mode = 'row',
    isClone = true
  }: {
    targetRow?: T
    targetIndex?: number
    newData?: T | T[]
    rowKey?: string
    isClone?: boolean
    mode?: 'row' | 'dataIndex' | 'visibleIndex' | 'unshift' | 'push'
  } = {}
): Promise<ReturnPromiseType<VxeGridInstance['insertAt']> | null> {
  if ($grid) {
    let rowIndex: number
    const { fullData, visibleData } = $grid.getTableData()
    let target: T | T[] | null | -1
    if (isClone) {
      newData = clone(newData, true)
    }
    switch (mode) {
      case 'row':
        rowIndex = $grid.getVTRowIndex(targetRow)
        // index + 1: 在下一行的上方插入 -1: 没有下一行,在最后插入
        target = rowIndex === -1 ? -1 : visibleData[rowIndex + 1] || -1
        break
      // 数据的索引
      case 'dataIndex':
        rowIndex = $grid.getVTRowIndex(fullData[targetIndex])
        target = rowIndex === -1 ? -1 : visibleData[rowIndex + 1] || -1
        break
      // 显示的索引
      case 'visibleIndex': {
        target = visibleData[targetIndex + 1] || -1
        break
      }
      case 'unshift':
        target = null
        break
      case 'push':
        target = -1
        break
      default:
        console.warn('新增行失败: 未知mode')
        return null
    }
    // 在最后删除rowKey, 因为targetRow和newData可能是同一个对象(复制),提前删除会导致rowKey丢失,getVTRowIndex无法索引新增位置
    if (isArray(newData)) {
      newData.forEach((v) => {
        removeTableRowKey($grid, v, false, rowKey)
      })
    } else {
      removeTableRowKey($grid, newData, false, rowKey)
    }
    // console.log('新增行: ', newData, target);
    return await $grid.insertAt(newData, target)
  } else {
    console.warn('新增行失败: grid不存在')
    return null
  }
}

/**
 * 删除行
 * 删除位置为目标行(显示为准)
 * @param $grid
 * @param options.targetRow 目标行 只对 mode=row 有效
 * @param options.targetIndex 目标索引 只对 mode=dataIndex, visibleIndex 有效
 * @param options.mode 目标行定位模式
 */
export async function removeTableRow<T extends Recordable>(
  $grid: VxeGridInstance<T>,
  {
    targetRow,
    targetIndex = 0,
    mode = 'row'
  }: {
    targetRow?: T | T[]
    targetIndex?: number | number[]
    mode?: 'row' | 'dataIndex' | 'visibleIndex' | 'shift' | 'pop' | 'visibleAll' | 'fullAll'
  } = {}
): Promise<ReturnPromiseType<VxeGridInstance['remove']> | null> {
  if ($grid) {
    const { fullData, visibleData } = $grid.getTableData()
    let target: T | T[] | null
    switch (mode) {
      case 'row': {
        if (targetRow) {
          target = targetRow
        } else {
          console.warn('删除行失败: targetRow不存在')
          return null
        }
        break
      }
      // 数据的索引
      case 'dataIndex': {
        if (isArray(targetIndex)) {
          target = targetIndex.map((index) => fullData[index])
        } else {
          target = fullData[targetIndex]
        }
        break
      }
      // 显示的索引
      case 'visibleIndex': {
        if (isArray(targetIndex)) {
          target = targetIndex.map((index) => visibleData[index])
        } else {
          target = visibleData[targetIndex]
        }
        break
      }
      case 'shift': {
        target = visibleData[0]
        break
      }
      case 'pop': {
        target = visibleData[visibleData.length - 1]
        break
      }
      case 'visibleAll': {
        target = visibleData
        break
      }
      case 'fullAll': {
        target = null
        break
      }
      default: {
        console.warn('删除行失败: 未知mode')
        return null
      }
    }
    // console.log('删除行: ', target);
    return await $grid.remove(target)
  } else {
    console.warn('删除行失败: grid不存在')
    return null
  }
}

// TODO
export async function upTableRow<T extends Recordable>(
  $grid: VxeGridInstance<T>,
  {
    targetRow,
    targetIndex = -1,
    mode = 'row'
  }: {
    targetRow?: T
    targetIndex?: number
    newData?: T | T[]
    rowKey?: string
    isClone?: boolean
    mode?: 'row' | 'dataIndex' | 'visibleIndex'
  } = {}
): Promise<ReturnPromiseType<VxeGridInstance['insertAt']> | null> {
  if ($grid) {
    let prevRow: T | T[] | null | -1
    let currentRow: T | T[] | undefined
    let currentRowIndex: number
    const { fullData, visibleData } = $grid.getTableData()
    switch (mode) {
      case 'row':
        currentRow = targetRow
        currentRowIndex = $grid.getVTRowIndex(currentRow)
        prevRow = currentRowIndex === -1 ? -1 : visibleData[currentRowIndex - 1] || -1
        break
      // 数据的索引
      case 'dataIndex':
        currentRow = fullData[targetIndex]
        currentRowIndex = $grid.getVTRowIndex(currentRow)
        prevRow = currentRowIndex === -1 ? -1 : visibleData[currentRowIndex - 1] || -1
        break
      // 显示的索引
      case 'visibleIndex': {
        prevRow = visibleData[targetIndex - 1] || -1
        break
      }
      default:
        console.warn('上移行失败: 未知mode')
        return null
    }
    if (prevRow !== -1) {
      await $grid.remove(currentRow)
      return await $grid.insertAt(currentRow, prevRow)
    } else {
      // console.warn('上移行失败: 已在顶部');
      return null
    }
  } else {
    console.warn('上移行失败: grid不存在')
    return null
  }
}

// TODO
export async function downTableRow<T extends Recordable>(
  $grid: VxeGridInstance<T>,
  {
    targetRow,
    targetIndex = -1,
    mode = 'row'
  }: {
    targetRow?: T
    targetIndex?: number
    newData?: T | T[]
    rowKey?: string
    isClone?: boolean
    mode?: 'row' | 'dataIndex' | 'visibleIndex'
  } = {}
): Promise<ReturnPromiseType<VxeGridInstance['insertAt']> | null> {
  if ($grid) {
    let currentRow: T | T[] | undefined
    let currentRowIndex: number
    let nextRow: T | T[] | null | -1
    const { fullData, visibleData } = $grid.getTableData()
    switch (mode) {
      case 'row':
        currentRow = targetRow
        currentRowIndex = $grid.getVTRowIndex(currentRow)
        nextRow = currentRowIndex === -1 ? -1 : visibleData[currentRowIndex + 1] || -1
        break
      // 数据的索引
      case 'dataIndex':
        currentRow = fullData[targetIndex]
        currentRowIndex = $grid.getVTRowIndex(currentRow)
        nextRow = currentRowIndex === -1 ? -1 : visibleData[currentRowIndex + 1] || -1
        break
      // 显示的索引
      case 'visibleIndex': {
        nextRow = visibleData[targetIndex + 1] || -1
        break
      }
      default:
        console.warn('下移行失败: 未知mode')
        return null
    }
    if (nextRow !== -1) {
      await $grid.remove(nextRow)
      return await $grid.insertAt(nextRow, currentRow)
    } else {
      // console.warn('下移行失败: 已在底部');
      return null
    }
  } else {
    console.warn('下移行失败: grid不存在')
    return null
  }
}
